/**
 * $Id: CellRenderer.java,v 1.12 2012-01-13 11:34:10 david Exp $
 * Copyright (c) 2007-2012, JGraph Ltd
 */
package com.kg.rails.component.editor.util;

import com.kg.rails.component.editor.canvas.Canvas;
import com.kg.rails.component.editor.canvas.*;
import com.kg.rails.component.editor.view.Graph;
import com.kg.rails.component.editor.view.GraphView;
import com.kg.rails.component.editor.view.TemporaryCellStates;
import org.w3c.dom.Document;

import java.awt.*;
import java.awt.image.BufferedImage;

public class CellRenderer {
    private CellRenderer() {
        // static class
    }

    /**
     * Draws the given cells using a Graphics2D canvas and returns the buffered image
     * that represents the cells.
     *
     * @param graph Graph to be painted onto the canvas.
     * @return Returns the image that represents the canvas.
     */
    public static Canvas drawCells(Graph graph, Object[] cells,
                                   double scale, EditorRectangle clip, CanvasFactory factory) {
        Canvas canvas = null;

        if (cells == null) {
            cells = new Object[]{graph.getModel().getRoot()};
        }

        // Gets the current state of the view
        GraphView view = graph.getView();

        // Keeps the existing translation as the cells might
        // be aligned to the grid in a different way in a graph
        // that has a translation other than zero
        boolean eventsEnabled = view.isEventsEnabled();

        // Disables firing of scale events so that there is no
        // repaint or update of the original graph
        view.setEventsEnabled(false);

        // Uses the view to create temporary cell states for each cell
        TemporaryCellStates temp = new TemporaryCellStates(view, scale,
                cells);

        try {
            if (clip == null) {
                clip = graph.getPaintBounds(cells);
            }

            if (clip != null && clip.getWidth() > 0 && clip.getHeight() > 0) {
                Rectangle rect = clip.getRectangle();
                canvas = factory.createCanvas(rect.width + 1, rect.height + 1);

                if (canvas != null) {
                    double previousScale = canvas.getScale();
                    Point previousTranslate = canvas.getTranslate();

                    try {
                        canvas.setTranslate(-rect.x, -rect.y);
                        canvas.setScale(view.getScale());

                        for (int i = 0; i < cells.length; i++) {
                            graph.drawCell(canvas, cells[i]);
                        }
                    } finally {
                        canvas.setScale(previousScale);
                        canvas.setTranslate(previousTranslate.x,
                                previousTranslate.y);
                    }
                }
            }
        } finally {
            temp.destroy();
            view.setEventsEnabled(eventsEnabled);
        }

        return canvas;
    }

    public static BufferedImage createBufferedImage(Graph graph,
                                                    Object[] cells, double scale, Color background, boolean antiAlias,
                                                    EditorRectangle clip) {
        return createBufferedImage(graph, cells, scale, background, antiAlias,
                clip, new Graphics2DCanvas());
    }

    public static BufferedImage createBufferedImage(Graph graph,
                                                    Object[] cells, double scale, final Color background,
                                                    final boolean antiAlias, EditorRectangle clip,
                                                    final Graphics2DCanvas graphicsCanvas) {
        ImageCanvas canvas = (ImageCanvas) drawCells(graph, cells, scale,
                clip, new CanvasFactory() {
            public Canvas createCanvas(int width, int height) {
                return new ImageCanvas(graphicsCanvas, width, height,
                        background, antiAlias);
            }

        });

        return (canvas != null) ? canvas.destroy() : null;
    }

    public static Document createHtmlDocument(Graph graph, Object[] cells,
                                              double scale, Color background, EditorRectangle clip) {
        HTMLCanvas canvas = (HTMLCanvas) drawCells(graph, cells, scale,
                clip, new CanvasFactory() {
            public Canvas createCanvas(int width, int height) {
                return new HTMLCanvas(DomUtils.createHtmlDocument());
            }

        });

        return canvas.getDocument();
    }

    public static Document createSvgDocument(Graph graph, Object[] cells,
                                             double scale, Color background, EditorRectangle clip) {
        SVGCanvas canvas = (SVGCanvas) drawCells(graph, cells, scale, clip,
                new CanvasFactory() {
                    public Canvas createCanvas(int width, int height) {
                        return new SVGCanvas(DomUtils.createSvgDocument(width,
                                height));
                    }

                });

        return canvas.getDocument();
    }

    public static Document createVmlDocument(Graph graph, Object[] cells,
                                             double scale, Color background, EditorRectangle clip) {
        VMLCanvas canvas = (VMLCanvas) drawCells(graph, cells, scale, clip,
                new CanvasFactory() {
                    public Canvas createCanvas(int width, int height) {
                        return new VMLCanvas(DomUtils.createVmlDocument());
                    }

                });

        return canvas.getDocument();
    }

    public static abstract class CanvasFactory {

        /**
         * Separates the creation of the canvas from its initialization, when the
         * size of the required graphics buffer / document / container is known.
         */
        public abstract Canvas createCanvas(int width, int height);
    }
}
